---
title: VContainer + ECS (beta)
sidebar_label: ECS (beta)
---

VContainer supports integration between Unity's ECS (Entity Component System) and regular C# World.
( This is an experimental feature. Any feedback is wellcome! :0 )

:::caution
Currently, this feature requires Unity 2019.3 or later versions.
:::

## Setup

ECS features for VContainer is enabled if the project has the `com.unity.entities` package installed.

- Currently, ECS is a preview version. You may need the settings: `[Windows] -> [Package Manager]` and `[Advanced] -> [Show preview packages]`.
- Select package of `Entities` and Press `[Install]`.

If the `com.unity.entities` package exists, the `VCONTAINER_ECS_INTEGRATION` symbol is defined and the following features are enabled.

## When using Unity's default World

By default, ECS will automatically instantiate classes that inherits the `ComponentSystemBase` defined in your project, add it to the default world, and running on it.

In this mode, you can use method injection to ECS `System`.
( The constructor is automatically used by Unity, so it cannot be used.)

```csharp
class SystemA : SystemBase
{
    [Inject]
    public void Construct(Settings settings)
    {
        // ...
    }

    protected override void OnUpdate()
    {
        // ...
    }

}
```

```csharp
// Inject the `System` to Unity's default World
builder.RegisterSystemFromDefaultWorld<SystemA>();

// builder.RegisterSystemFromDefaultWorld<SystemB>();
// builder.RegisterSystemFromDefaultWorld<SystemC>();

// Other dependencies can be injected into the System.
builder.RegisterInstance(settings);
// ...
```

(Optional) The above can also be declared by grouping as below:

```csharp
builder.UseDefaultWorld(systems =>
{
    systems.Add<SystemA>();

    // systems.Add<SystemB>();
    // systems.Add<SystemC>();
    // ...
});
```

Internaly, this is an automation of the following processes:

```csharp
var system = World.DefaultGameObjectInjectionWorld.GetExistingSystem<SystemA>();
system.Construct(settings);
```

:::note
In default (`UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP` is **not** used), The SystemGroup to which the System belongs can be controlled by Attribute ( e.g: `[UpdateInGroup(typeof(SystemGroupType))]` .
:::


### Example of setup entities (with Default World)

```csharp
public class GameLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        builder.UseDefaultWorld(systems =>
        {
            systems.Add<SystemA>();
        })
    }
}
```

```csharp
public class SystemA : SystemBase
{
    [Inject]
    public void Construct(Foo foo)
    {
        // Setup entities...
        var archtype = World.EntityManager.CreateArchetype(typeof(ComponentDataA));
        World.EntityManager.CreateEntity(archtype);
    }
    protected override void OnUpdate()
    {
        Entities.ForEach((ref ComponentDataA data) =>
            {
                // Do something...
            })
            .Schedule();
    }
}
```

## When to use your custom world

ECS also allows you to create and register your own system.

There are two ways to disable Unity's automatic system bootstrap.

- By setting the define symbol `UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP` will disable all World and System auto-bootstrap.
- Or, By adding the `[DisableAutoCreation]` attribute to the class definition to disable auto bootstrap per system.

For System that have auto bootstrap disabled, constructor injection can be used.

```csharp
public class SystemA : SystemBase
{
    readonly ServiceA serviceA;

    // Constructor injection
    public SampleSystem(ServiceA serviceA)
    {
        this.serviceA = serviceA;
    }

    protected override void OnUpdate()
    {
        // ...
    }
}
```

To use this System, you need to set up World yourself.

VContainer supports to instantiate of World and configuration.

```csharp
// Register of new World under the control of VContainer.
builder.RegisterNewWorld("My World 1", Lifetime.Scoped);

// Register System by specifying the name of World to be added.
builder.RegisterSystemIntoWorld<SystemA>("My World 1");
// builder.RegisterSystemIntoWorld<SystemB>("My World 1");
// builder.RegisterSystemIntoWorld<SystemC>("My World 1");

// Other dependencies can be injected into the System.
builder.Register<ServiceA>(Lifetime.Singleton);
```

(Optional) The above can also be declared by grouping as below:

```csharp
builder.UseNewWorld("My World 1", Lifetime.Scoped, systems =>
{
    systems.Add<SystemA>();

    // systems.Add<SystemB>();
    // systems.Add<SystemC>();
    // ...
});
```

Internaly, If you use above methods, the following setup will be performed automatically:

```csharp title="YourLifetimeScope.cs"
// When resolving world ...

var world = new World("My World 1");

world.CreateSystem<InitializationSystemGroup>();
world.CreateSystem<SimulationSystemGroup>();
world.CreateSystem<PresentationSystemGroup>();

ScriptBehaviourUpdateOrder.UpdatePlayerLoop(world, PlayerLoop.GetCurrentPlayerLoop());

// Resolving dependencies ...
var systemA = new SystemA(new ServiceA());
world.AddSystem(systemA);

var systemGroup = (ComponentSystemGroup)world.GetOrCreateSystem<SimulationSystemGroup>();
systemGroup.AddSystemToUpdateList(systemA);


// After container build ...

foreach (var system in world.Systems)
{
    if (system is ComponentSystemGroup group)
        group.SortSystems();
}
```

:::note
- Currently, VContainer is registering the World using `ScriptBehaviourUpdateOrder.UpdatePlayerLoop`.
- This is an alias that registers 3 SystemGroups to PlayerLoop, so VContainer also creates these SystemGroups internally.
:::

By default, VContainer will register System to `SimulationSystemGroup`. If you want to change this, you can use `.IntoGroup<T>()`:

```csharp
// Example
builder.RegisterSystemIntoWorld<SystemA>("My World 1")
    .IntoGroup<PresentationSystemGroup>();
```

#### Lifeime of World and Systems

`RegisterNewWorld(...)` or `UseNewWorld(...)` can accept Lifetime as an argument.

- This new World  is placed under the control of VContainer.
- World holds System. Therefore, the lifetime of System is the same as World.
- If `Lifetime.Scoped` is specified, when the scope is destroyed, Dispose of all the systems belonging to that World will be called.


```csharp
builder.RegisterNewWorld("My World 1", Lifetime.Scoped);
builder.RegisterSystemIntoWorld("My World 1");
```

```csharp
public class SystemA : SystemBase, IDisposable
{
    protected override void OnUpdate()
    {
        // ...
    }

    // Called when scope is disposed.
    public void Dispose()
    {
        // ...
    }
}
```

### Example of setup entities (with Custom World)

```csharp
public class GameLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        builder.UseNewWorld("My World 1", Lifetime.Scoped, systems =>
        {
            systems.Add<SystemA>();
        })
    }
}
```

```csharp
public class SystemA : SystemBase
{
    public void SystemA(Foo foo)
    {
        // Injected dependencies...
    }

    protected override void OnCreate()
    {
        // Setup entities...
        var archtype = World.EntityManager.CreateArchetype(typeof(ComponentDataA));
        World.EntityManager.CreateEntity(archtype);
    }

    protected override void OnUpdate()
    {
        Entities.ForEach((ref ComponentDataA data) =>
            {
                // Do something...
            })
            .Schedule();
    }
}
```

## Resolving `World`

```csharp
// When only one world is registered:
class ClassA
{
    public ClassA(World world) { /* ... */ }
}

// When multiple worlds is registered
class ClassA
{
    public ClassA(IEnumerable<World> worlds)
    {
        var world = worlds.First(x => x.Name == "My new world");
        // ...
    }
}
```
